package ui;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import core.*;

public class PresentationPanel extends JPanel implements ActionListener, core.ChangeListener {
	
	private static final long serialVersionUID = 1L;
	private Presentable[] songs;
	private char[][] keyBindings;
	private int curSong, curSlide;
	private ScreenDisplay pres;
	private JToggleButton blankButton;
	private JButton imgButton;
	private BufferedImage bgImg;
	private Preferences prefs;
	
	/*
	public PresentationPanel(SetRecord sr, int monitor) {
		setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
		pres = new ScreenDisplay(monitor);
		songs = sr.getItems();
		curSong = curSlide = 0;
		
		initializeKeyBindings();
		initializeView();
		
		setVisible(true);
	}
	*/
	public PresentationPanel(SetRecord sr, SongIndex ind, Preferences prefs) {
		setLayout(new BorderLayout());
		pres = new ScreenDisplay(prefs);
		songs = sr.getItems(ind);
		curSong = curSlide = 0;
		this.prefs  = prefs;
		
		initializeKeyBindings();
		initializeToolbar();
		initializeView();
		
		toggleBlankState();		// blank the screen
		
		// load the last image
		if (!prefs.lastImageLocation.exists()) {
			prefs.lastImageLocation = null;
		} else if (prefs.lastImageLocation != null) {
			try {
				bgImg = ImageIO.read(prefs.lastImageLocation);
				pres.setBackgroundImage(bgImg);
			} catch (IOException e1) {
				JOptionPane.showMessageDialog(PresentationPanel.this, "Error opening file: " + prefs.lastImageLocation.getPath());
				e1.printStackTrace();
			}
		}
		
		setVisible(true);
	}
	
	// save/close action handler
	public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand().equalsIgnoreCase("close"))
			pres.dispose();
	}
	
	private void initializeToolbar() {
		JToolBar toolbar = new JToolBar();
		toolbar.setFloatable(false);
		
		blankButton = new JToggleButton("Blank Screen");
		blankButton .addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				pres.setBlank(blankButton.isSelected());
			}
		});
		toolbar.add(blankButton);
		
		bgImg = null;
		imgButton = new JButton("Image");
		imgButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JFileChooser fc = new JFileChooser();
				fc.setFileFilter(new FileNameExtensionFilter("Image Files", "bmp", "gif", "jpg", "jpeg", "png"));
				int rv = fc.showOpenDialog(PresentationPanel.this);
				
				if (rv == JFileChooser.APPROVE_OPTION) {
					prefs.lastImageLocation = fc.getSelectedFile();
					try {
						bgImg = ImageIO.read(fc.getSelectedFile());
						pres.setBackgroundImage(bgImg);
					} catch (IOException e1) {
						JOptionPane.showMessageDialog(PresentationPanel.this, "Error opening file: " + fc.getSelectedFile());
						e1.printStackTrace();
						return;
					}
					prefs.save();
				}
			}
		});
		toolbar.add(imgButton);
		
		final JButton clearImgButton = new JButton("Clear Image");
		clearImgButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				pres.setBackgroundImage(null);
			}
		});
		toolbar.add(clearImgButton);
		
		add(toolbar, BorderLayout.NORTH); 
	}
	
	private void toggleBlankState() {
		pres.setBlank(!pres.isBlank());
		blankButton.setSelected(!blankButton.isSelected());
	}
	
	private void initializeView() {
		
		// build tree nodes of songs 
		final DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode();
		// 1st-level node: song title 
		for (Presentable p : songs) {
			DefaultMutableTreeNode n = new DefaultMutableTreeNode(p.getTitle());
			// 2nd-level node: slide title
			for (int i=0; i<p.getSlideCount(); i++) {
				String attrib = p.getSlide(i).getAttribute(),
						elem = p.getSlide(i).getElement(),
						text = p.getSlide(i).getText(),
						// if there is no sub-title (attribute), display first line
						firstLine = text.substring(0, (text.indexOf('\n') == -1 ? text.length() : text.indexOf('\n'))),
						str = (elem.isEmpty() ? "" : elem + " - ") 
								+ (attrib.isEmpty() ? firstLine : attrib);
				n.add(new DefaultMutableTreeNode(str));
			}
			treeRoot.add(n);
		}
		
		// create slide display
		final JTextArea previewDisp = new JTextArea();
		//previewDisp.setFont(new Font("Lucida Unicode", Font.PLAIN, 13));
		previewDisp.setEditable(false);
		previewDisp.setFocusable(false);
		JScrollPane previewView = new JScrollPane(previewDisp);
		previewView.setPreferredSize(new Dimension(400, 360));
		
		
		// create tree display
		final JTree itemDisp = new JTree(treeRoot);
		//itemDisp.setFont(new Font("Lucida Unicode", Font.PLAIN, 13));
		itemDisp.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		itemDisp.setRootVisible(false);
		for (int i=0; i<itemDisp.getRowCount(); i++)		// expand all rows
			itemDisp.expandRow(i);
		removeKeyListeners(itemDisp);		// prevent default behavior of selecting any entry when key pressed
		itemDisp.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0) {
					switch (e.getKeyCode()) {
					case KeyEvent.VK_B:
						toggleBlankState();
						break;
					}
				}
				else {	
					DefaultMutableTreeNode selected;
					if (itemDisp.getSelectionPath() == null)
						selected = null;
					else 
						selected = (DefaultMutableTreeNode)itemDisp.getSelectionPath().getLastPathComponent();
					
					switch (e.getKeyCode()) {

					// do nothing: these are already handled by tree selection listener
					case KeyEvent.VK_UP: case KeyEvent.VK_DOWN:
					case KeyEvent.VK_LEFT: case KeyEvent.VK_RIGHT:
						break;
					
					case KeyEvent.VK_ESCAPE: {
						pres.dispose();
						Component p = PresentationPanel.this.getParent();
						if (p instanceof JTabbedPane) {
							//int i = ((JTabbedPane)p).indexOfComponent(PresentationPanel.this);
							((JTabbedPane)p).remove(PresentationPanel.this);
						}
						break;
					}
					
					// toggle the expand/collapse state of the current song
					case KeyEvent.VK_ENTER: 
						if (selected != null){							
							TreePath path = new TreePath(selected.getPath());
							if (selected.getLevel() == 1) { // if a song was selected
								if (itemDisp.isExpanded(path))
									itemDisp.collapsePath(path);
								else
									itemDisp.expandPath(path);
							}
							else
								toggleBlankState();
						}
						break;
					
					// find and select a slide bound with the key pressed
					default: 
						if (selected != null) {
							if (selected.getLevel() == 1)
								curSong = treeRoot.getIndex(selected);
							int nextSlide = findNextSlide(Character.toLowerCase(e.getKeyChar()));
							if (nextSlide != -1) {
								curSlide = nextSlide;
								itemDisp.setSelectionPath(new TreePath(((DefaultMutableTreeNode)treeRoot.getChildAt(curSong).getChildAt(curSlide)).getPath()));
							}
						}
					} // end switch
				}
			}
		});
		itemDisp.addTreeSelectionListener(new TreeSelectionListener() {
			
			// update the current song and current slide
			public void valueChanged(TreeSelectionEvent e) {
				DefaultMutableTreeNode selected = (DefaultMutableTreeNode)e.getPath().getLastPathComponent();
				
				// if a slide was selected, update the selection values
				if (selected.getLevel() > 1)	{
					curSong = treeRoot.getIndex(selected.getParent());
					curSlide = selected.getParent().getIndex(selected);
					previewDisp.setText(songs[curSong].getSlide(curSlide).getText());
					// display the selected slide
					pres.setText(songs[curSong].getSlide(curSlide).getText());
					blankButton.setSelected(false);
				}
				// else if a song title was selected, show the whole text in preview
				else {
					int tmpSong = treeRoot.getIndex(selected);
					if (songs[tmpSong] instanceof SongRecord)
						previewDisp.setText(((SongRecord)songs[tmpSong]).getLyrics());
				}
				
				itemDisp.scrollPathToVisible(e.getPath());
				previewDisp.setCaretPosition(0);	// scroll to the top of preview display
				
				//System.out.println("selected path: " + e.getPath());
				//System.out.println("song, slide: " + curSong + ", " + curSlide);
			}
		});
		JScrollPane treeView = new JScrollPane(itemDisp);
//		treeView.setPreferredSize(new Dimension(260, 360));
		treeView.setMinimumSize(new Dimension(160, 360));
		
		JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treeView, previewView);
//		sp.setDividerLocation(260);
		//add(treeView, BorderLayout.WEST);
		//add(previewView, BorderLayout.CENTER);
		add(sp, BorderLayout.CENTER);
	}
	
	private void initializeKeyBindings() {
		keyBindings = new char[songs.length][];
		for (int i=0; i<keyBindings.length; i++) {
			keyBindings[i] = new char[songs[i].getSlideCount()];
			for (int j=0; j<keyBindings[i].length; j++) { 
				String elem = songs[i].getSlide(j).getElement();
				keyBindings[i][j] = Character.toLowerCase(elem.length()>0 ? elem.charAt(0) : ' ');
			}
		}
	}
	
	private static void removeKeyListeners(JComponent c) {
		KeyListener[] kls = c.getKeyListeners();
		for(int i=0; i<kls.length; i++)
			c.removeKeyListener(kls[i]);
	}

	private int findNextSlide(char c) {
		// search from current slide to end
		for (int i=curSlide+1; i<keyBindings[curSong].length; i++)
			if (keyBindings[curSong][i] == c)
				return i;
		// if not found, search from beginning to current slide
		for (int i=0; i<=curSlide && i<keyBindings[curSong].length; i++)
			if (keyBindings[curSong][i] == c)
				return i;
		return -1;
	}
	
	/* Called when presentation (the Presentable object) is changed */
	public void presentationChanged(int type) {
		
	}
	
/*	public static void main(String[] args) {
		JFrame fr = new JFrame();
		SetRecord r = new SetRecord(-1);
		
		File[] files = new File("data/").listFiles(new FilenameFilter(){
			public boolean accept(File dir, String name) {
				return (name.contains(".txt"));
			}
		});
		for (File f : files)
			r.add(new SongRecord(0, f));
//		fr.add(new PresentationPanel(r, 0));
		fr.add(new PresentationPanel(r, null, new Preferences()));
		fr.pack();
		fr.setVisible(true);
		fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}*/
	
}
